﻿using System.ComponentModel.Composition;
using AppsGenerator.Generators;
using DBBuilder.DBDataStructure;
using CommonLogging;
using AppsGenerator.Models;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System;
using System.Text;
using AppsGenerator.Utils;

namespace AppsGenerator.WebGenerators.WebModelGenerator
{
    /// <summary>
    /// The generator is responsible for generating ViewModel component
    /// </summary>
    [Export(typeof(ViewModelGenerator))]
    public sealed class ViewModelGenerator : BaseGenerator, IWebComponentsGenerator
    {
        #region Fields

        /// <summary>
        /// The instance of _classComponents, this object will be updated when deserialization pattern file
        /// </summary>
        private ClassComponents _classComponents;

        /// <summary>
        /// The logger instance
        /// </summary>
        public readonly ILogger Logger = LoggerManager.GetLogger(typeof(ViewModelGenerator).Name);

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor
        /// </summary>
        public ViewModelGenerator()
        {
            this.XMLTemplate = "ViewModelTemplate.xml";
            this.DirPath = Path.Combine(this.GetAppPath(), typeof(WebGenerator).Name, "ViewModels");
            this.InitializeDir(this.DirPath);
            this.DeserializeTemplate();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Override this method to generate components
        /// </summary>
        /// <param name="db">Data source for generating</param>
        public void Generate(DataBase db)
        {
            this.GenerateContents(db);
        }

        /// <summary>
        /// Deserialize pattern file
        /// </summary>
        private void DeserializeTemplate()
        {
            var patternFile = Path.Combine(this.GetAppPath(), "Templates", "WebTemplates", this.XMLTemplate);

            using (XmlTextReader xreader = new XmlTextReader(patternFile))
            {
                xreader.Namespaces = false;
                try
                {
                    var x = new XmlSerializer(typeof (ClassComponents));
                    this._classComponents = x.Deserialize(xreader) as ClassComponents;
                }
                catch (Exception ex)
                {
                    throw new Exception(string.Format("Cannot deserialize ViewModelTemplate, ViewModelTemplate => {0}",
                                                      ex.Message));
                }
            }
        }

        /// <summary>
        /// Method for generating contents of controller components and save generated contents to a file
        /// </summary>
        /// <param name="db">Data source for generating</param>
        private void GenerateContents(DataBase db)
        {
            this.Logger.Info("Generate ViewModel --> Start");
            Console.WriteLine("Generate ViewModel --> Start");

            try
            {
                var contents = new StringBuilder(BaseConsts.MAX_BYTES);

                // Generating Using region
                contents.AppendLine(this._classComponents.UsingPart);
                contents.Replace(BaseConsts.ConstGlobalNameSpace, this.GlobalNameSpace);
                contents.AppendLine(string.Empty);

                // Generating Class region
                contents.AppendLine(string.Format("namespace {0}.ViewModels", this.GlobalNameSpace));
                contents.Append("{");
                contents.AppendLine(this._classComponents.ClassPart.Comment);
                contents.Append(BaseConsts.TAB1).AppendLine("public class GroupViewModel");
                contents.Append(BaseConsts.TAB1).AppendLine("{");

                // Generating Regions Fields
                contents.Append(this.GenerateRegionFields(db));

                contents.Append(BaseConsts.TAB1).AppendLine("}");
                contents.Append("}");
                contents.AppendLine();

                // Save controller contents to a file
                this.SaveFile(contents.ToString().TrimStart(), "GroupViewModel.cs");
            }
            catch (Exception ex)
            {
                this.Logger.ErrorFormat("GenerateContents Error: {0}", ex.ToString());
                throw new Exception(ex.ToString());
            }

            this.Logger.Info("Generate ViewModel <-- End");
            Console.WriteLine("Generate ViewModel <-- End");
        }

        /// <summary>
        /// The method for generating region fields
        /// </summary>
        /// <param name="db">Data source for generating</param>
        /// <returns></returns>
        private string GenerateRegionFields(DataBase db)
        {
            var contents = new StringBuilder(BaseConsts.MAX_BYTES);

            // Generating Regions Fields
            contents.Append(BaseConsts.TAB2).AppendLine("#region Fields");
            contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);

            foreach (Table tbl in db.Tables)
            {
                // Skips a Table if it is existent in Skip array
                if (!this.IsItemExistent(BaseConsts.SkipTables, tbl.Name))
                {
                    var tblName = WebConfiguration.Instance.Settings.UserTable.Equals(tbl.Name)
                                      ? "Custom" + tbl.Name
                                      : tbl.Name;
                    contents.Append(BaseConsts.TAB2).AppendLine("/// <summary>");
                    contents.Append(BaseConsts.TAB2).AppendLine(
                        string.Format("/// The list of {0} object which retrieved from database", tbl.Name));
                    contents.Append(BaseConsts.TAB2).AppendLine("/// </summary>");
                    contents.Append(BaseConsts.TAB2).AppendLine(string.Format("public List<{0}> {1} {{ get; set; }}",
                                                                              tblName,
                                                                              WebConfiguration.Instance.Settings.
                                                                                  UserTable.Equals(tbl.Name)
                                                                                  ? ("Custom" + tbl.Name).
                                                                                        Pluralize()
                                                                                  : tbl.Name.Pluralize()));
                    contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);
                }
            }

            contents.Append(BaseConsts.TAB2).AppendLine("#endregion");
            contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);

            return contents.ToString();
        }

        #endregion
    }
}
